package com.jiankangjin.httpclient;

import java.io.InputStream;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;

import javax.net.ssl.SSLContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.EntityBuilder;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;

/**
 * 带证书的HttpClient客户端
 * @author liuzy
 * @date 2014年10月30日 上午10:05:44
 */
public class HttpClient {
	private static final transient Log logger = LogFactory.getLog(HttpClient.class);
	
	private HttpPost httpPost = null;
	private CloseableHttpClient httpClient = null;
	private RequestConfig requestConfig = null;
	private int connectionRequestTimeout = 9999;
	private int socketTimeout = 9999;
	private int connectTimeout = 9999;
	private UrlEncodedFormEntity entity;
	private int statusCode = 0;
	private String charset = "UTF8";
	private Map<String, String> propertiesMap = null;
	private static HttpClient instance = null;
	private static HttpClient instanceSSL = null;
	
	/**
	 * 创建默认的HttpClient客户端
	 */
	public static synchronized HttpClient getInstance() {
		if (instance == null) {
			return new HttpClient();
		} else {
			return instance;
		}
	}
	
	/**
	 * 创建带证书的HttpClient客户端
	 * @param cerStream 证书流
	 * @param type 证书类型
	 * @param password 证书密码
	 */
	public static synchronized HttpClient getInstance(InputStream cerStream, String type, String password) {
		if (instanceSSL == null) {
			return new HttpClient(cerStream, type, password);
		} else {
			return instanceSSL;
		}
	}

	/**
	 * 创建默认的HttpClient客户端
	 */
	private HttpClient() {
		init();
		this.httpClient = HttpClients.createDefault();
	}
	
	/**
	 * 创建带证书的HttpClient客户端
	 * @param KEYstream 证书流
	 * @param type 证书类型
	 * @param password 证书密码
	 */
	private HttpClient(InputStream cerStream, String type, String password) {
		init();
		try {
			KeyStore keyStore  = KeyStore.getInstance(type);
			keyStore.load(cerStream, password.toCharArray());
			SSLContext sslcontext = SSLContexts.custom()
					.loadKeyMaterial(keyStore, password.toCharArray())
					.build();
			SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
					sslcontext,
					new String[] { "TLSv1" },
					null,
					SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
			this.httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
		} catch (Exception e) {
			logger.error("证书加载错误");
		}
	}
	
	/**
	 * 加载配置文件中的超时设置
	 */
	private void init() {
		propertiesMap = PropertiesLoader.loade();
		String time = "";
		try {
			if (propertiesMap != null) {
				time = propertiesMap.get("connectionRequestTimeout");
				this.connectionRequestTimeout = Integer.parseInt(time);
				time = propertiesMap.get("socketTimeout");
				this.connectionRequestTimeout = Integer.parseInt(time);
				time = propertiesMap.get("connectTimeout");
				this.connectionRequestTimeout = Integer.parseInt(time);
				requestConfig = RequestConfig.custom()
						.setConnectionRequestTimeout(connectionRequestTimeout)
						.setSocketTimeout(socketTimeout)
						.setConnectTimeout(connectTimeout)
						.build();
			}
		} catch (NumberFormatException e) {
			logger.error("HTTP超时时间配置错误,将设置为9999ms,错误配置=" + time);
		}
	}
	
	//-----------------------------------------------------------------------------

	/**
	 * HttpClient的POST请求
	 * @param url 请求的地址
	 * @param params 请求的键值参数
	 * @return 把地址返回的JSON转为Map
	 */
	public Map<String, String> doPost(String url, Map<String, String> params) {
		String JSONstr = doPost(url, params, this.charset);
		return fromJSON(JSONstr);
	}
	
	/**
	 * HttpClient的POST请求
	 * @param url 请求的地址
	 * @param JSON 请求的参数为JSON字符串
	 * @return 把地址返回的JSON转为Map
	 */
	public Map<String, String> doPost(String url, String JSON) {
		String JSONstr = doPost(url, JSON, this.charset);
		return fromJSON(JSONstr);
	}
	
	/**
	 * HttpClient的POST请求
	 * @param url 请求的地址
	 * @param binary 请求的参数为字节数据,会以流传到url
	 * @return 把地址返回的JSON转为Map
	 */
	public Map<String, String> doPost(String url, byte[] binary) {
		String JSONstr = doPost(url, binary, this.charset);
		return fromJSON(JSONstr);
	}

	/**
	 * HttpClient的POST请求
	 * @param url 请求的地址
	 * @param params 请求的键值参数
	 * @param charset 要求返回时转换为某字符集
	 * @return 返回请求后的实体内容字符串
	 */
	public String doPost(String url, Map<String, String> params, String charset) {
		HttpEntity httpEntity = buildHttpEntity(params);
		HttpPost httpPost = new HttpPost(url);
		httpPost.setEntity(httpEntity);
		return post(httpPost);
	}	
	
	/**
	 * HttpClient的POST请求
	 * @param url 请求的地址
	 * @param JSON 请求的参数为JSON字符串
	 * @param charset 要求返回时转换为某字符集
	 * @return 返回请求后的实体内容字符串
	 */
	public String doPost(String url, String JSON, String charset) {
		Map<String, String> params = fromJSON(JSON);
		return doPost(url, params, charset);
	}
	
	/**
	 * HttpClient的POST请求
	 * @param url 请求的地址
	 * @param binary 请求的参数为字节数据,会以流传到url
	 * @param charset 要求返回时转换为某字符集
	 * @return 返回请求后的实体内容字符串
	 */
	public String doPost(String url, byte[] binary, String charset) {
		HttpEntity httpEntity = buildHttpEntity(binary);
		HttpPost httpPost = new HttpPost(url);
		httpPost.setEntity(httpEntity);
		return post(httpPost);
	}

	/**
	 * 根据键值参数,创建请求的实体
	 * @param params 键值参数
	 * @return 请求的实体HttpEntity
	 */
	private HttpEntity buildHttpEntity(Map<String, String> params) {
		List<NameValuePair> nvp = new ArrayList<NameValuePair>();
        for (String key : params.keySet()) {
            nvp.add(new BasicNameValuePair(key, params.get(key)));
        }
        try {
        	return new UrlEncodedFormEntity(nvp, this.charset);
		} catch (Exception e) {
			return null;
		}
	}
	
	/**
	 * 根据字节数据,创建请求的实体
	 * @param binary 字节数据
	 * @return 请求的实体HttpEntity
	 */
	private HttpEntity buildHttpEntity(byte[] binary) {
		return EntityBuilder.create().setBinary(binary).build();
	}
	
	/**
	 * 执行POST
	 * @param httpPost HttpClient执行的httpPost
	 * @return Post请求的结果内容
	 */
	private String post(HttpPost httpPost) {
		try {
			httpPost.setConfig(requestConfig);
			HttpResponse httpResponse = httpClient.execute(httpPost);
			statusCode = httpResponse.getStatusLine().getStatusCode();
			if (statusCode == HttpStatus.SC_OK) {
				HttpEntity httpEntity = httpResponse.getEntity();
				return EntityUtils.toString(httpEntity, this.charset);
			} else {
				return null;
			}
		} catch (Exception e) {
			logger.error(this, e);
			return null;
		}
	}
	
	/**
	 * JSON字符串转为Map对象
	 * @param JSON JSON字符串
	 * @return 键值对Map对象
	 */
	private Map<String, String> fromJSON(String JSON) {
		try {
			return new ObjectMapper().readValue(JSON, new TypeReference<HashMap<String, String>>() {});
		} catch (Exception e) {
			logger.error(this, e);
			return null;
		}
	}

	
	//-----------------------------------------------------------------------------
	
	
	/**
	 * doPOST 
	 * @param path 第一种:/项目名/服务接口名  
	 *             第二种:http://...
	 * @param params
	 * @return 
	 */
	public Map<String, Object> doPOSTgetHashMap(String path, Map<String, Object> params) {
		try {
			String JSONstr = null;
			if (path.startsWith("/")) {
				String[] str = path.split("/");
				JSONstr = doPOSTgetStr(str[1], str[2], params);
			} else if(path.startsWith("http://") || path.startsWith("https://")) {
				JSONstr = doPOSTgetStr(path, params);
			} else {
				throw new IllegalArgumentException("path参数不合法,当前path="+path);
			}
			if (null == JSONstr) {
				return null;
			}
			@SuppressWarnings("unchecked")
			Map<String, Object> map = new ObjectMapper().readValue(JSONstr, HashMap.class);
			return map;
		} catch (Exception e) {
			logger.error(this, e);
			return null;
		}
	}

	/**
	 * doPOSTgetStr 
	 * @param url http开头的地址
	 * @param params
	 * @return 
	 */
	public String doPOSTgetStr(String url, Map<String, Object> params) {
		try {
			httpPost = new HttpPost(url);
			if (params !=null && !params.isEmpty()) {
				entity = new UrlEncodedFormEntity(convert(params), "UTF-8");
				httpPost.setEntity(entity);
			}
			return beginPOST(httpPost);
		} catch (Exception e) {
			logger.error(this, e);
			return null;
		}
	}

	/**
	 * @param url http开头的地址
	 * @param params
	 * @param charset utf8 gbk
	 * @return
	 */
	public String doPOSTgetStr(String url, Map<String, Object> params, String charset) {
		if (charset.toUpperCase().equals("GBK")) {
			this.charset = "GBK";
		}
		try {
			httpPost = new HttpPost(url);
			if (params !=null && !params.isEmpty()) {
				entity = new UrlEncodedFormEntity(convert(params), "UTF-8");
				httpPost.setEntity(entity);
			}
			return beginPOST(httpPost);
		} catch (Exception e) {
			logger.error(this, e);
			return null;
		}
	}

	/**
	 * doPOSTgetStr 
	 * @param project 项目名
	 * @param method 接口服务名
	 * @param params
	 * @return 
	 */
	public String doPOSTgetStr(String project, String method, Map<String, Object> params) {
		try {
			String url = null;
			if (propertiesMap != null) {
				url = propertiesMap.get(project);
			}
			if (url == null) {
				throw new IllegalArgumentException("项目"+project+"的url未配置");
			}
			if(!checkIP(url.split(":")[0]) || !checkPort(url.split(":")[1])) {
				throw new IllegalArgumentException("项目"+project+"配置不合法,当前url="+url);
			}
			httpPost = new HttpPost("http://"+url + "/"+project+"/" +method);
			if (params != null && !params.isEmpty()) {
				entity = new UrlEncodedFormEntity(convert(params), "UTF-8");
				httpPost.setEntity(entity);
			}
			return beginPOST(httpPost);
		} catch (Exception e) {
			logger.error(this, e);
			return null;
		}
	}
	//返回内容，公司项目返回JSON
	private String beginPOST (HttpPost httpPost) {
		try {
			httpPost.setConfig(requestConfig);
			HttpResponse httpResponse = httpClient.execute(httpPost);
			statusCode = httpResponse.getStatusLine().getStatusCode();
			if (statusCode == HttpStatus.SC_OK) {
				HttpEntity httpEntity = httpResponse.getEntity();
				String str = EntityUtils.toString(httpEntity, this.charset);
				return str;
			} else {
				return null;
			}
		} catch (Exception e) {
			logger.error(this, e);
			return null;
		}
	}
	// 将传过来的参数填充到List<NameValuePair>中
	private List<NameValuePair> convert (Map<String, Object> paramsHashMap) {
		List<NameValuePair> params = new ArrayList<NameValuePair>();
		if (paramsHashMap != null && !paramsHashMap.isEmpty()) {
			for (Entry<String, Object> entry : paramsHashMap.entrySet()) {
				params.add(new BasicNameValuePair(entry.getKey(),
						entry.getValue()==null ? "" : ((Object)entry.getValue()).toString()));
			}
			return params;
		}
		return null;
	}

	private boolean checkIP(String str) {
		String regex = "^((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5]"
                        + "|[*])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5]|[*])$";
		return Pattern.matches(regex, str);
    }
	private boolean checkPort(String str) {
		String regex = "^[1-9]$|(^[1-9][0-9]$)|(^[1-9][0-9][0-9]$)|(^[1-9][0-9][0-9][0-9]$)|(^[1-6][0-5][0-5][0-3][0-5]$)";
		return Pattern.matches(regex, str);
    }
}